package services;

import java.util.List;

import models.User;
import play.Logger;
import play.cache.Cache;
import play.libs.F.Promise;
import play.mvc.Http.Session;
import services.CollectorService.Services;
import util.UserSession;
import exceptions.FilYOUInstalationException;
import exceptions.NoUUIDException;

/**
 * @author marcio
 * 
 *         This class holds all installation and authentication business logic
 * */
public final class UserService {
	/** static instance of the class called by the {@link #getInstance()} */
	private static UserService instance = null;

	/**
	 * Empty constructor to guarantee that this constructor can only be called
	 * from within this class
	 * */
	private UserService() {
		// Empty constructor
	}

	/**
	 * Static method for recovering the {@link UserAuthService} singleton
	 * instance without having to instantiate an object HTTP requests
	 * 
	 * @return The singleton instance.
	 */
	public static UserService getInstance() {
		if (instance == null) {
			instance = new UserService();
		}

		return instance;
	}

	/**
	 * Get existing user session or creates a new one if doesn't exist
	 * 
	 * @param session
	 * @return the user session
	 * @throws NoUUIDException
	 */
	public UserSession getUserSession(Session session) throws NoUUIDException {
		String uuid = session.get("uuid");
		// if the session object has no uuid, create one
		if (uuid == null) {
			uuid = java.util.UUID.randomUUID().toString();
			session.put("uuid", uuid);
		}
		// try to find a user session on cache
		UserSession userSession = (UserSession) Cache.get(uuid + "_user");
		// if session is not found create
		if (userSession == null) {
			userSession = createNewUserSession(uuid);
		}
		return userSession;
	}

	/**
	 * Update existing user session.
	 * 
	 * @param userSession
	 *            the user session object to be udpate.
	 * @return the updated user session object
	 * */
	public UserSession updateExistingUserSession(UserSession userSession) {
		Cache.set(userSession.getUUID() + "_user", userSession);
		return (UserSession) Cache.get(userSession.getUUID() + "_user");
	}

	/**
	 * Create a new user session
	 * 
	 * @param uuid
	 * @return the user session that was just created
	 * */
	public UserSession createNewUserSession(String uuid) {
		UserSession userSession = new UserSession();
		userSession.setUUID(uuid);
		Cache.set(userSession.getUUID() + "_user", userSession);
		Logger.debug("Usersession.createNewUserSession: New user session chached in memory " + uuid);
		return userSession;
	}

	/**
	 * @return The user object or <code>null</code> if the user exists but password is wrong
	 * @throws FilYOUInstalationException 
	 * */
	public User createOrSignin(String username, String password, Session session)
			throws FilYOUInstalationException {
		User user = User.finder.where().eq("username", username).findUnique();
		try {
			if (user != null) {
				if (user.validatePassword(password)) {
					UserSession userSession = getUserSession(session);
					userSession.setUser(user);
					updateExistingUserSession(userSession);
				} else {
					user = null;
				}
			} else {
				user = new User();
				user.username = username;
				user.setPassword(password);
				// TODO: put a URL
				user.pictureUrl = "/assets/img/headshot.png";

				// create users in the collectors
				// TODO this should be handled closer to a controller,
				//       so that we could return a AsyncResponse and free the thread.
				Promise<List<Long>> promises = CollectorService.createUsers(servicesUsed);
				List<Long> ids = promises.get();
				loadCollectorIds(user, ids);
				
				UserSession userSession = getUserSession(session);
				userSession.setUser(user);
				updateExistingUserSession(userSession);
			}
			
			user.save();
		} catch (NoUUIDException e) {
			e.printStackTrace();
		} catch (java.lang.Exception e2) {
			e2.printStackTrace();
			throw new FilYOUInstalationException();
		}

		return user;
	}
	
	public User createOrSigninFacebook(String userId, Session session) throws FilYOUInstalationException{
		User user = User.finder.where().eq("fbId", userId).findUnique();
		try {
			if (user == null) {
				user = new User();
				user.username = userId; //TODO replace it!
				user.fbId = Long.parseLong(userId);
				user.setPassword(userId + "abcde");
				// TODO: put a URL
				user.pictureUrl = "/assets/img/headshot.png";

				// create users in the collectors
				// TODO this should be handled closer to a controller,
				//       so that we could return a AsyncResponse and free the thread.
				Promise<List<Long>> promises = CollectorService.createUsers(servicesUsed);
				List<Long> ids = promises.get();
				loadCollectorIds(user, ids);
			}
			user.save();
			
			UserSession userSession = getUserSession(session);
			userSession.setUser(user);
			updateExistingUserSession(userSession);
		} catch (NoUUIDException e) {
			e.printStackTrace();
		} catch (java.lang.Exception e2) {
			e2.printStackTrace();
			throw new FilYOUInstalationException();
		}

		return user;
	}
	
	private Services[] servicesUsed = new Services[] { Services.CALENDAR,
			Services.EMAIL, Services.FILE, Services.PEOPLE };

	public void loadCollectorIds(User user, List<Long> ids) {
		user.calendaryouId = ids.get(0);
		user.emaiyouId = ids.get(1);
		user.filyouId = ids.get(2);
		user.peopleyouId = ids.get(3);
		
		user.save();
	}
}
